package cn.siriusbot.rpc.server;

import cn.siriusbot.rpc.SiriusBotRpcServer;
import cn.siriusbot.rpc.SiriusLogger;
import cn.siriusbot.rpc.entity.RpcBody;
import cn.siriusbot.rpc.entity.RpcError;
import cn.siriusbot.siriuspro.error.MsgException;
import com.alibaba.fastjson2.JSONObject;
import lombok.SneakyThrows;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 基于ws连接的rpc服务端
 */
public class WebsocketSiriusBotRpcServer extends BasicsSiriusBotRpcServer implements SiriusBotRpcServer {


    private String wsUrl;

    private WebSocketClient webSocket;
    /**
     * 重连状态，为true的时候
     */
    AtomicBoolean reconnectionState = new AtomicBoolean(false);

    @SneakyThrows
    public WebsocketSiriusBotRpcServer(String siriusUrl, String packageName, String verify, SiriusLogger log) {
        super(log);
        this.wsUrl = String.format("%s/expand-websocket/%s", formatUrl(siriusUrl), packageName);
        connect(wsUrl, verify);
    }

    @SneakyThrows
    public WebsocketSiriusBotRpcServer(String siriusUrl, String packageName, String verify) {
        super();
        this.wsUrl = String.format("%s/expand-websocket/%s", formatUrl(siriusUrl), packageName);
        connect(wsUrl, verify);
    }

    private String formatUrl(String urlString) throws MalformedURLException {
        URL url = new URL(urlString);
        String protocol = "ws://";
        String host = url.getHost();
        int port = url.getPort();
        if (port != -1) {
            host += ":" + port;
        }
        return protocol + host;
    }


    /**
     * 连接
     */
    @SneakyThrows
    private void connect(String wsUrl, String verify) {
        this.webSocket = new WebSocketClient(new URI(wsUrl)) {
            @Override
            public void onOpen(ServerHandshake handshakedata) {
                log.info("WS已连接到天狼星框架，正在发送验证包");
                this.send(verify);
            }

            @Override
            public void onMessage(String message) {
                RpcBody rpcBody;
                try {
                    rpcBody = JSONObject.parseObject(message, RpcBody.class);
                    if (rpcBody.getId() == null) return;
                    if (rpcBody.getApi() == null) return;
                    if (rpcBody.getMethod() == null) return;
                } catch (Throwable ignored) {
                    return;
                }
                // 序列化成功，开始处理
                RpcBody resultBody = new RpcBody().setId(rpcBody.getId());
                try {
                    final Object result = getResult(rpcBody);
                    // 发送正常结果
                    resultBody.setResult(result);

                } catch (Throwable e) {
                    Throwable item = e;
                    while (!(item instanceof MsgException) && item.getCause() != null) {
                        item = item.getCause();
                    }
                    final RpcError rpcError = new RpcError().setErrorId(UUID.randomUUID().toString());
                    rpcError.setMessage(item.getMessage());
                    log.error(String.format("程序调用错误，错误标记码：%s%n", rpcError.getErrorId()), e);
                    resultBody.setError(rpcError);
                }
                send(JSONObject.toJSONString(resultBody));
            }

            @Override
            public void onClose(int code, String reason, boolean remote) {
                log.warn("机器人服务器连接被断开,正在尝试重连...");
                reconnection(); // 重连
            }

            @Override
            public void onError(Exception ex) {
                log.warn("连接机器人服务器异常，嵌套异常为 -> " + ex);
                log.warn("正在尝试重连...");
                reconnection();// 重连
            }
        };
        this.webSocket.connect();
    }

    /**
     * 重连
     */
    private void reconnection() {
        try {
            this.webSocket.close();
        } catch (Throwable ignored) {

        }
        synchronized (this) {
            if (!reconnectionState.get()) {
                reconnectionState.set(true);
                new Thread(() -> {
                    try {
                        Thread.sleep(3000);
                        this.webSocket.reconnect();
                    } catch (Throwable ignored) {

                    } finally {
                        reconnectionState.set(false);
                    }
                }).start();
            }
        }
    }
}
